#include "kernel.h"
#include "../mds/sys/string.h"

static
char * ocl_kernel_string(cl_kernel      kernel,
						 cl_kernel_info info)
{
	char  *s      = NULL;
	size_t length = 0;
	
	ocl_errno = CL_SUCCESS;
	if( CL_SUCCESS != ( ocl_errno = clGetKernelInfo(kernel, info, 0, NULL, &length ) ) )
	{
		return NULL;
	}
	assert(length>0);
	
	if( NULL == ( s = mds_string_create( length-1 ) ) )
	{
		return NULL;
	}
	
	if( CL_SUCCESS != ( ocl_errno =  clGetKernelInfo(kernel, info, length, s, NULL) ) )
	{
		mds_string_delete(s);
		return NULL;
	}
	
	return s;
}


int ocl_kernel_load( ocl_kernel *kernel, ocl_program *program, const char *name )
{
	static const char fn[] = "[ocl_kernel_load]";
	assert( kernel  );
	assert( program );
	assert( name != NULL );
	
	ocl_errno   = CL_SUCCESS;
	memset( kernel, 0, sizeof(ocl_kernel) );
	if( ocl_logfile ) fprintf( ocl_logfile, "%s '%s'\n",fn, name );
	
	/**********************/
	/* create the kernel */
	/********************/
	kernel->handle = clCreateKernel( program->handle, name, & ocl_errno  );
	if( CL_SUCCESS != ocl_errno )
		return -1;
	
	if( NULL == ( kernel->FUNCTION_NAME = ocl_kernel_string( kernel->handle, CL_KERNEL_FUNCTION_NAME ) ) )
	{
		goto ERROR;
	}
	if( ocl_logfile ) fprintf( ocl_logfile, "%s FUNCTION_NAME= '%s'\n", fn, kernel->FUNCTION_NAME );
	
	if( CL_SUCCESS != ( ocl_errno = clGetKernelInfo( kernel->handle, CL_KERNEL_NUM_ARGS, sizeof(cl_uint), & kernel->NUM_ARGS, NULL) ) )
	{
		goto ERROR;
	}
	if( ocl_logfile ) fprintf( ocl_logfile, "%s NUM_ARGS = %u\n",fn, kernel->NUM_ARGS );
	
	return 0;
	
ERROR:
	ocl_kernel_quit( kernel );
	return -1;
}

void ocl_kernel_quit( ocl_kernel *kernel )
{
	if( kernel )
	{
		mds_string_delete( kernel->FUNCTION_NAME);
		(void)clReleaseKernel( kernel->handle );
		memset( kernel, 0, sizeof(kernel) );
	}
}

int ocl_kernel_set_arg( ocl_kernel *kernel, cl_uint arg_index, size_t arg_size, const void *arg_value )
{
	
	assert( kernel );
	assert( kernel->FUNCTION_NAME );
	ocl_errno   = CL_SUCCESS;
	if( ocl_logfile ) fprintf( ocl_logfile, "[ocl_kernel_set_arg] %s.arg[%u]= @%p+<%u>\n", kernel->FUNCTION_NAME, arg_index, arg_value, (unsigned)arg_size );
	if( CL_SUCCESS != ( ocl_errno = clSetKernelArg( kernel->handle, arg_index, arg_size, arg_value ) ) )
	{
		return -1;
	}
	return 0;
}

int  ocl_kernel_set_arg_buffer( ocl_kernel *kernel, cl_uint arg_index, ocl_buffer *buffer )
{
	assert( kernel );
	assert( kernel->FUNCTION_NAME );
	ocl_errno   = CL_SUCCESS;
	if( ocl_logfile ) fprintf( ocl_logfile, "[ocl_kernel_set_arg] %s.arg[%u]= __global<%u>\n", kernel->FUNCTION_NAME, arg_index, (unsigned)( buffer->SIZE ) );
	if( CL_SUCCESS != ( ocl_errno = clSetKernelArg( kernel->handle, arg_index, sizeof(cl_mem), & (buffer->handle) ) ) )
	{
		return -1;
	}
	return 0;
}

int  ocl_kernel_set_arg_local( ocl_kernel *kernel, cl_uint arg_index, size_t bytes  )
{
	assert( kernel );
	assert( kernel->FUNCTION_NAME );
	ocl_errno   = CL_SUCCESS;
	if( ocl_logfile ) fprintf( ocl_logfile, "[ocl_kernel_set_arg] %s.arg[%u]= __local<%u>\n", kernel->FUNCTION_NAME, arg_index, (unsigned) bytes );
	if( CL_SUCCESS != ( clSetKernelArg( kernel->handle, arg_index, bytes, NULL) ) )
	{
		return -1;
	}
	return 0;
}


int ocl_get_kernel_work_group_size( const ocl_kernel *kernel, const ocl_device *device, size_t *wgs )
{
	assert( kernel != NULL);
	assert( kernel->handle );
	assert( device != NULL);
	assert( device->id );
	assert( wgs != NULL );
	
	if( CL_SUCCESS != ( ocl_errno = clGetKernelWorkGroupInfo(kernel->handle, 
															 device->id, 
															 CL_KERNEL_WORK_GROUP_SIZE, 
															 sizeof(size_t), 
															 wgs, 
															 NULL) ) )
	{
		*wgs = 0;
		return -1;
	}
	return 0;
}


